﻿/// <reference path="Scripts/jquery-2.0.2.min.js" />


var MineSweeper = (function () {
    "use strict";

    var Block = Class.create({
        constr: function (rowIndex, colIndex) {
            this.rowIndex = rowIndex;
            this.colIndex = colIndex;
            this.initContent = "";
            this.isMine = false;
            this.isOpen = false;
            this.adjacentMines = 0;
        },
        render: function () {
            var td = $("<td/>");
            td.text(this.initContent);
            return td;
        }
    });

    var Row = Class.create({
        constr: function (rowIndex, colsCount) {
            this.blocks = [];
            this.colsCount = colsCount;
            this.rowIndex = rowIndex;
            this.addBlocks();
        },
        addBlocks: function () {
            var block, colIndex;
            for (colIndex = 0; colIndex < this.colsCount; colIndex++) {
                block = new Block(this.rowIndex, colIndex);
                this.blocks.push(block);
            }
        },
        render: function () {
            var i, tr = $("<tr/>");
            for (i = 0; i < this.blocks.length; i++) {
                tr.append(this.blocks[i].render());
            }

            return tr;
        }
    });

    var Board = Class.create({
        constr: function (rowsCount, colsCount) {
            this.rows = [];
            this.rowsCount = rowsCount;
            this.colsCount = colsCount;
            this.addRows();
        },
        addRows: function () {
            var row, rowIndex;
            for (rowIndex = 0; rowIndex < this.rowsCount; rowIndex++) {
                row = new Row(rowIndex, this.colsCount);
                this.rows.push(row);
            }
        },
        render: function () {
            var i, table = $("<table/>");
            for (i = 0; i < this.rows.length; i++) {
                table.append(this.rows[i].render());
            }

            return table;
        },
    });

    var Minesweeper = Class.create({
        constr: function (element, rowsCount, colsCount, minesCount, level) {
            this.element = element;
            this.board = new Board(rowsCount, colsCount);
            this.rowsCount = rowsCount;
            this.colsCount = colsCount;
            this.minesCount = minesCount;
            this.remainingMines = minesCount;
            this.allBlocks = rowsCount * colsCount;
            this.blocksToOpen = this.allBlocks - minesCount;
            this.colors = ["#0100FE", "#017F01", "#FE0000", "#010080", "#810102", "#008081", "#000000", "#808080"];
            this.timer = new Timer($("#timer"));
            this.scoreboard = new Scoreboard();
            this.scoreboard.load();
            this.level = level;

            this.putMines();
            this.calculateAdjacentMines();
            this.renderRemainingMines(this.remainingMines);
            this.renderBlocksToOpen(this.blocksToOpen);
        },
        putMines: function () {
            var currentMines = 0, block, row, col;

            while (currentMines < this.minesCount) {
                row = Math.floor((Math.random() * this.board.rowsCount));
                col = Math.floor((Math.random() * this.board.colsCount));
                block = this.board.rows[row].blocks[col];

                if (block.isMine === false) {
                    block.isMine = true;
                    currentMines++;
                }
            }
        },
        calculateAdjacentMines: function () {
            var dirRow = [-1, -1, 0, 1, 1, 1, 0, -1];
            var dirCol = [0, 1, 1, 1, 0, -1, -1, -1];
            var block, rowIndex, colIndex, dir, adjRowIndex, adjColIndex;

            for (rowIndex = 0; rowIndex < this.board.rowsCount; rowIndex++) {
                for (colIndex = 0; colIndex < this.board.colsCount; colIndex++) {
                    block = this.board.rows[rowIndex].blocks[colIndex];

                    if (block.isMine) {
                        continue;
                    }

                    for (dir = 0; dir < dirRow.length; dir++) {
                        adjRowIndex = rowIndex + dirRow[dir];
                        adjColIndex = colIndex + dirCol[dir];

                        if (this.isInBoardRange(adjRowIndex, adjColIndex) && this.board.rows[adjRowIndex].blocks[adjColIndex].isMine) {
                            block.adjacentMines++;
                        }
                    }
                }
            }
        },
        renderRemainingMines: function (mines) {
            $("#remaining-mines").text(mines + "/" + this.minesCount);
        },
        renderBlocksToOpen: function (blocks) {
            $("#remaining-blocks").text(blocks + "/" + (this.allBlocks - this.minesCount));
        },
        addEvents: function () {
            var that = this;

            // Open
            $(this.element).on("click", "td", function () {
                var clickedBlock = $(this);

                if (clickedBlock.hasClass("open") || clickedBlock.hasClass("mine")) {
                    return;
                }

                if (clickedBlock.hasClass('flag')) {
                    that.remainingMines++;
                    that.renderRemainingMines(that.remainingMines);
                }

                // Open mode
                clickedBlock.removeClass();
                clickedBlock.addClass("open");

                // Get coordinates of clicked block
                var colIndex = clickedBlock.parent().children().index($(this));
                var rowIndex = clickedBlock.parent().parent().children().index($(this).parent());
                that.checkBlockType(rowIndex, colIndex);
            });

            // Flag & Question
            $(this.element).on("contextmenu","td", function (event) {
                event.preventDefault();
                var clickedBlock = $(this);

                if (clickedBlock.hasClass("open") || clickedBlock.hasClass("mine")) {
                    return;
                }

                $.playSound('Sounds/flag.mp3');

                if (clickedBlock.hasClass("flag")) {
                    // Question mode
                    clickedBlock.removeClass("flag");
                    clickedBlock.addClass("question");
                    that.remainingMines++;
                    that.renderRemainingMines(that.remainingMines);
                    return;
                }
                else if (clickedBlock.hasClass("question")) {
                    // Empty mode
                    clickedBlock.removeClass("question");
                    return;
                }
                else {
                    // Flag mode
                    clickedBlock.addClass("flag");
                    that.remainingMines--;
                    that.renderRemainingMines(that.remainingMines);
                }
            });

            // Pause
            $(document).keydown(function (ev) {
                if (ev.keyCode === 80) {
                    that.showGameDialog("Game is pause.", "PAUSE", {
                        "Continue": function () {
                            $(this).dialog('destroy');
                            $(".dialog-message").remove();
                            that.timer.start();
                        }
                    }, false);

                    $(".ui-dialog-titlebar-close").on('click', function () {
                        that.timer.start();
                    });
                }

                that.timer.stop();
            });
        },
        removeEvents: function () {
            $("td").off('click');
            $("td").off('contextmenu');
            $(document).off('keydown');
        },
        checkBlockType: function (rowIndex, colIndex) {
            var cell, block = this.board.rows[rowIndex].blocks[colIndex];

            if (block.isMine) {
                // Game over
                this.gameOver();
            }
            else if (block.adjacentMines > 0) {
                // Block with adjacent mines
                cell = this.getCellByGivenCoordinates(rowIndex, colIndex);
                cell.text(block.adjacentMines);
                cell.css('color', this.colors[block.adjacentMines - 1]);
                $.playSound('Sounds/click.mp3');
                this.blocksToOpen--;
                this.renderBlocksToOpen(this.blocksToOpen);

                // Game won
                if (this.blocksToOpen == 0) {
                    this.gameWon();
                }
            }
            else {
                // Empty block
                this.openAllEmptyAdjacentBlocks(rowIndex, colIndex);
                $.playSound('Sounds/opening.mp3');
            }
        },
        isInBoardRange: function (rowIndex, colIndex) {
            if ((0 <= rowIndex && rowIndex < this.board.rowsCount) && (0 <= colIndex && colIndex < this.board.colsCount)) {
                return true;
            }

            return false;
        },
        getCellByGivenCoordinates: function (rowIndex, colIndex) {
            var cell = $(this.element).first().find('tr').eq(rowIndex).find('td').eq(colIndex);
            return cell;
        },
        openAllEmptyAdjacentBlocks: function (rowIndex, colIndex) {
            var dirRow = [-1, -1, 0, 1, 1, 1, 0, -1];
            var dirCol = [0, 1, 1, 1, 0, -1, -1, -1];
            var currentBlock, adjacentBlock, cell, adjRowIndex, adjColIndex, i;

            var queue = new Queue();
            queue.enqueue(this.board.rows[rowIndex].blocks[colIndex]);

            while (queue.length() > 0) {
                currentBlock = queue.dequeue();
                this.blocksToOpen--;
                this.renderBlocksToOpen(this.blocksToOpen);

                for (i = 0; i < dirRow.length; i++) {
                    adjRowIndex = currentBlock.rowIndex + dirRow[i];
                    adjColIndex = currentBlock.colIndex + dirCol[i];

                    if (this.isInBoardRange(adjRowIndex, adjColIndex)) {
                        adjacentBlock = this.board.rows[adjRowIndex].blocks[adjColIndex];
                        cell = this.getCellByGivenCoordinates(adjacentBlock.rowIndex, adjacentBlock.colIndex);

                        if (cell.hasClass("open")) {
                            continue;
                        }

                        if (cell.hasClass('flag')) {
                            this.remainingMines++;
                            this.renderRemainingMines(this.remainingMines);
                        }

                        cell.removeClass();
                        cell.addClass('open');

                        if (adjacentBlock.adjacentMines === 0) {
                            adjacentBlock.adjacentMines = -1;
                            queue.enqueue(adjacentBlock);

                        }
                        else if (adjacentBlock.adjacentMines > 0) {
                            cell.text(adjacentBlock.adjacentMines);
                            cell.css('color', this.colors[adjacentBlock.adjacentMines - 1]);
                            this.blocksToOpen--;
                            this.renderBlocksToOpen(this.blocksToOpen);
                        }
                    }
                }
            }

            if (this.blocksToOpen == 0) {
                this.gameWon();
            }
        },
        showAllMines: function () {
            var rowIndex, colIndex, cell;
            for (rowIndex = 0; rowIndex < this.rowsCount; rowIndex++) {
                for (colIndex = 0; colIndex < this.colsCount; colIndex++) {
                    if (this.board.rows[rowIndex].blocks[colIndex].isMine) {
                        cell = this.getCellByGivenCoordinates(rowIndex, colIndex);
                        cell.addClass("mine");
                    }
                }
            }
        },
        startGame: function () {
            var gameBoard = this.board.render();
            $(this.element).append(gameBoard);
            this.addEvents();
            this.timer.clear();
            this.timer.start();
        },
        gameOver: function () {
            this.showAllMines();
            $.playSound('Sounds/explode.mp3');
            $.playSound('Sounds/lose.mp3');
            this.renderRemainingMines(this.remainingMines);
            this.timer.stop();
            this.removeEvents();

            this.showGameDialog("Some text", "Game over!", {
                "Try again": function () {
                    location.reload();
                },
                "Show ranking": function () {
                    // Show ranking
                }
            }, true);
        },
        gameWon: function () {
            this.showAllMines();
            $.playSound('Sounds/win.mp3');
            this.renderRemainingMines(this.remainingMines);
            this.timer.stop();
            this.removeEvents();

            var time = this.timer.getSeconds();
            var that = this;
            this.showGameDialog("WON in " + time + " seconds!", "WON!!!", {
                "Add result to ranking": function () {
                    that.addResultToScoreboard("nasmda", time);
                    that.showScoreboard();
                    $(this).dialog('destroy');
                },
                "Try again": function () {
                    location.reload();
                }
            }, true);
        },
        addResultToScoreboard: function (name, time) {
            this.scoreboard.levels[this.level].addResult(name, time);
        },
        showScoreboard: function () {
            this.showGameDialog("asdasdweqweas", "Scoreboard", {
                "Try again": function () {
                    location.reload();
                }
            }, true);
        },
        showGameDialog: function (text, title, buttons, closeOnEscape) {
            var dialogElement = $("<div/>");
            dialogElement.addClass("dialog-message");
            dialogElement.text(text);
            $(this.element).append(dialogElement);

            dialogElement.dialog({
                title: title,
                modal: true,
                draggable: false,
                resizable: false,
                position: ['center', 'center'],
                show: 'blind',
                hide: 'blind',
                width: 400,
                dialogClass: 'game-dialog',
                buttons: buttons,
                closeOnEscape: closeOnEscape
            });
        },
    });

    return Minesweeper;
})();

var Timer = (function () {
    "use strict";

    var Timer = Class.create({
        constr: function (element) {
            this.element = $(element);
            this.seconds = 0;
            this.timerId;
        },
        start: function () {
            var that = this;
            this.element.parent().show();

            if (!this.timerId) {
                this.timerId = window.setInterval(function () {
                    that.seconds++;
                    that.element.text(that.seconds);
                }, 1000);
            }
        },
        stop: function () {
            window.clearInterval(this.timerId);
            this.timerId = null;
        },
        clear: function () {
            this.seconds = 0;
            this.element.text(0);
            this.stop();
        },
        restart: function () {
            this.seconds = 0;

            window.clearInterval(this.timerId);
            this.start();
        },
        getSeconds: function () {
            return this.seconds;
        }
    });

    return Timer;
})();

var Scoreboard = (function () {
    "use strict";

    var Result = Class.create({
        constr: function (name, score) {
            this.name = name;
            this.score = score;
        },
        render: function () {
            var $name = $("<td/>").text(this.name);
            var $score = $("<td/>").text(this.score);
            var $result = $("<tr/>");
            $result.append($name);
            $result.append($score);
            return $result;
        }
    });

    var Level = Class.create({
        constr: function (title) {
            this.title = title;
            this.results = [];
        },
        addResult: function (name, score) {
            var result = new Result(name, score);
            this.results.push(result);
        },
        sortResults: function () {
            this.results.sort(function (a, b) {
                return b.score - a.score;
            });
        },
        render: function () {
            this.sortResults();
            var $table = $("<table/>");
            for (var i = 0; i < this.results.length; i++) {
                $table.append(this.results[i].render());
            }

            return $table;
        }
    });

    var Scoreboard = Class.create({
        constr: function () {
            this.levels = [];
        },
        addLevel: function (title) {
            var level = new Level(title);
            this.levels.push(level);
        },
        render: function (element) {

            var $ul = $("<ul/>");
            for (var i = 0; i < this.levels.length; i++) {
                var $li = $("<li/>");
                var $a = $("<a/>");
                $a.attr("href", "#tabs-" + (i + 1));
                $a.text(this.levels[i].title);
                $li.append($a);
                $ul.append($li);
            }

            element.append($ul);


            for (i = 0; i < this.levels.length; i++) {
                var $div = $("<div/>");
                $div.append(this.levels[i].render());
                $div.attr('id', 'tabs-' + (i + 1));
                element.append($div);
            }

        },
        save: function () {
            localStorage.setItem("Scoreboard", JSON.stringify(this.levels));
        },
        load: function () {
            var levelsData = JSON.parse(localStorage.getItem("Scoreboard"));

            for (var i = 0; i < levelsData.length; i++) {
                this.addLevel(levelsData[i].title);

                for (var j = 0; j < levelsData[i].results.length; j++) {
                    this.levels[i].addResult(levelsData[i].results[j].name, levelsData[i].results[j].score)
                }
            }
        }
    });

    return Scoreboard;
})();

var Queue = Class.create({
    constr: function () {
        this.arr = [];
    },
    enqueue: function (elem) {
        this.arr.push(elem);
    },
    dequeue: function () {
        var retValue = this.arr[0];
        var newArr = new Array(this.arr.length - 1);
        for (var i = 0; i < newArr.length; i++) {
            newArr[i] = this.arr[i + 1];
        }

        this.arr = newArr;
        return retValue;
    },
    length: function () {
        return this.arr.length;
    }
});


var gameBoard = $("#game-board");

var minesweeper;
$("#begin-game").slideDown(1000);
$("#easy").on("click", function () {
    minesweeper = new MineSweeper(gameBoard, 7, 7, 1, 0);
    Start(minesweeper);
});

$("#medium").on("click", function () {
    minesweeper = new MineSweeper(gameBoard, 12, 20, 4);
    Start(minesweeper);
});

$("#hard").on("click", function () {
    minesweeper = new MineSweeper(gameBoard, 17, 25, 100);
    Start(minesweeper);
});

function Start(minesweeper) {
    minesweeper.startGame();
    $("#bottom-nav").css("display", "block");
    $("#begin-game").slideUp(1000);
}

//(function facebookShareing () {
//    window.fbAsyncInit = function () {
//        FB.init({
//            appId: '578335012188998',
//            status: true, // check login status
//            cookie: true, // enable cookies to allow the server to access the session
//            xfbml: true  // parse XFBML
//        });
//    };

//    (function () {
//        var e = document.createElement('script');
//        e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
//        e.async = true;
//        document.getElementById('fb-root').appendChild(e);
//    }());

//    $(document).ready(function () {
//        $('#share_button').on('click', function (e) {
//            e.preventDefault();
//            FB.ui(
//            {
//                method: 'feed',
//                name: 'TabPress1',
//                link: 'http://kiriltoshev.com/facebookshare/FacebookShare.html',
//                caption: 'I am a fan of TabPress',
//                description: 'TabPress -- Gotta love it!',
//                message: 'hello message'
//            });
//        });
//    });
//}());

//var scoreb = $("#scoreboard");
//var scoreboard = new Scoreboard(scoreb);
//scoreboard.addLevel("KEasy");
//scoreboard.levels[0].addResult("Ivan", 12);
//scoreboard.levels[0].addResult("Gosho", 18);
//scoreboard.levels[0].addResult("Pesho", 11);
//scoreboard.levels[0].addResult("Santos", 22);
//scoreboard.levels[0].addResult("Digo", 9);
//scoreboard.levels[0].addResult("Koko", 31);
//scoreboard.addLevel("Medium");
//scoreboard.levels[1].addResult("Ola", 12);
//scoreboard.levels[1].addResult("Gosho", 18);
//scoreboard.levels[1].addResult("Pesho", 11);
//scoreboard.levels[1].addResult("Santos", 22);
//scoreboard.levels[1].addResult("Digo", 9);
//scoreboard.levels[1].addResult("Koko", 31);

////scoreboard.load();

//scoreboard.render();

//scoreboard.save();

//scoreb.tabs();